package com.utils.file;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.utils.DateUtils;
import org.apache.tika.Tika;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipUtil {

    private static Logger logger = LoggerFactory.getLogger(ZipUtil.class);
    public static String localtion = "D://test//";
    public static String ERROR = "error";

    /**
     * 解压到目标路径
     *
     * @param srcFile  文件
     * @param savePath 目标路径
     * @return
     */
    public static void unZip(MultipartFile srcFile, String savePath) throws RuntimeException {
        FileOutputStream out = null;
        BufferedOutputStream bos = null;
        try {
            File file = null;
            InputStream ins = srcFile.getInputStream();
            File file2 = new File(savePath);
            if (!file2.exists()) {
                file2.mkdirs();//创建目录
            }
            file = new File(savePath + srcFile.getOriginalFilename());
            inputStreamToFile(ins, file);

            if (!file.exists()) {
                throw new RuntimeException(file.getPath() + ",file is not found");
            }
            //读取压缩文件
            ZipInputStream in = new ZipInputStream(new FileInputStream(file), Charset.forName("GBK"));
            //zip文件实体类
            ZipEntry entry;
            //遍历压缩文件内部 文件数量
            while ((entry = in.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    //文件输出流
                    out = new FileOutputStream(savePath + entry.getName());

                    bos = new BufferedOutputStream(out);
                    int len;
                    byte[] buf = new byte[1024];
                    while ((len = in.read(buf, 0, 1024)) != -1) {
                        bos.write(buf, 0, len);
                    }
                    bos.close();
                    out.flush();
                    out.close();
                }
            }
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取路径下所有的文件
     *
     * @param savePath 目标路径
     * @return
     */
    public List<File> getSubFiles(String savePath) {
        List<File> fileList = new ArrayList<>();
        File file = new File(savePath);
        File[] files = file.listFiles();
        for (File fileIndex : files) {
            if (!fileIndex.exists()) {
                throw new NullPointerException("Cannot find " + fileIndex);
            } else if (fileIndex.isFile()) {
                fileList.add(fileIndex);
            } else {
                if (fileIndex.isDirectory()) {
                    getSubFiles(fileIndex.getAbsolutePath());
                }
            }
        }
        return fileList;
    }

    /**
     * 将匹配到的非法字符以空替换
     *
     * @param fileName 获取的文件名
     * @return
     */
    public String illegalCharacterHandling(String fileName) {
        Pattern pattern = Pattern.compile("[\\s\\\\/:\\*\\?\\\"<>\\|]");
        Matcher matcher = pattern.matcher(fileName);
        return matcher.replaceAll("");
    }

    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[512];
            while ((bytesRead = ins.read(buffer, 0, 512)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除路径及路径下所有文件
     *
     * @param savePath 路径
     * @return
     */
    public static void clearFiles(String savePath) {
        File file = new File(savePath);
        deleteFile(file);
    }

    public static void deleteFile(File file) {
        if (file.exists()) {
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (int i = 0; i < files.length; i++) {
                    deleteFile(files[i]);
                }
            }
        }
        file.delete();
    }

    /**
     * 压缩
     *
     * @param urlList     文件路径集合
     * @return
     */
    public static ByteArrayOutputStream doFileZips(List<Map<String, String>> urlList) throws IOException {
        String url = "";
        int i = 1;
        ByteArrayOutputStream bos = null;
        ZipOutputStream zos = null;
        try {

            bos= new ByteArrayOutputStream();
            zos = new ZipOutputStream(bos);
            for (Map<String, String> oneFile : urlList) {
                Set<String> strings = oneFile.keySet();
                for (String key : strings) {
                    String value = oneFile.get(key);
                    zos.putNextEntry(new ZipEntry(i + "." + key));
                    byte[] bytes = getImageFromURL(value);
                    zos.write(bytes, 0, bytes.length);
                    zos.closeEntry();
                    i++;
                }
            }
            zos.close();
            bos.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bos;
    }

    /**
     * 写入文件
     *
     * @param bos
     * @param zipFileName 压缩文件名
     * @param savePath    路径
     * @return
     */
    public static File fileWrite(ByteArrayOutputStream bos, String zipFileName, String savePath) throws IOException {

        File file = new File(savePath + zipFileName);

        if (!file.exists()) {
            file.createNewFile();
        }

        OutputStream output = new FileOutputStream(file);

        BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);

        bufferedOutput.write(bos.toByteArray());

        bufferedOutput.close();

        output.close();

        return file;
    }

    public static byte[] getImageFromURL(String urlPath) {
        byte[] data = null;
        InputStream is = null;
        HttpURLConnection conn = null;
        try {
            URL url = new URL(urlPath);
            conn = (HttpURLConnection) url.openConnection();
            conn.setDoInput(true);
            // conn.setDoOutput(true);
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(6000);
            is = conn.getInputStream();
            if (conn.getResponseCode() == 200) {
                data = readInputStream(is);
            } else {
                data = null;
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            conn.disconnect();
        }
        return data;
    }

    public static byte[] readInputStream(InputStream is) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length = -1;
        try {
            while ((length = is.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            baos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        byte[] data = baos.toByteArray();
        try {
            is.close();
            baos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

    public static final String TIMESTAMP = DateUtil.format(new Date(), "yyyyMMddHHmmss");
    /**
     * 下载文件
     *
     * @param httpUrl 远程地址
     * @param localPath 本地路径
     * @return file
     */
    public static File getNetUrlHttp(String httpUrl,String localPath){
        //保存到本地的路径
        String filePath=localtion;
        File file = null;

        URL urlfile;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try{
            //判断文件的父级目录是否存在，不存在则创建
            file = new File(localPath);
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            try{
                //创建文件
                file.createNewFile();
            }catch (Exception e){
                e.printStackTrace();
            }
            //下载
            urlfile = new URL(httpUrl);
            inputStream = urlfile.openStream();
            outputStream = new FileOutputStream(file);

            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead=inputStream.read(buffer,0,8192))!=-1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (null != outputStream) {
                    outputStream.close();
                }
                if (null != inputStream) {
                    inputStream.close();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return file;
    }

    /**
     * 得到文件名称
     *
     * @param path 路径
     * @return {@link List}<{@link String}>
     */
    private List<String> getFileNames(String path) {
        File file = new File(path);
        if (!file.exists()) {
            return null;
        }
        List<String> fileNames = new ArrayList<>();
        return getFileNames(file, fileNames);
    }

    /**
     * 得到文件名称
     *
     * @param file      文件
     * @param fileNames 文件名
     * @return {@link List}<{@link String}>
     */
    private List<String> getFileNames(File file, List<String> fileNames) {
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                getFileNames(f, fileNames);
            } else {
                fileNames.add(f.getName());
            }
        }
        return fileNames;
    }

    /**
     * 得到文件
     *
     * @param path 路径
     * @return {@link List}<{@link String}>
     */
    public static List<File> getFileList(String path) {
        try {
            File file = new File(path);
            if (!file.exists()) {
                return null;
            }
            List<File> fileList = new ArrayList<>();
            return getFiles(file, fileList);
        }catch (Exception e){
            logger.error("获取文件出错!");
            return null;
        }
    }

    /**
     * 得到文件
     *
     * @param file      文件
     * @param fileList     文件列表
     * @return {@link List}<{@link String}>
     */
    public static List<File> getFiles(File file, List<File> fileList) {
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                getFiles(f, fileList);
            } else {
                fileList.add(f);
            }
        }
        return fileList;
    }
    
    /**
     * java8
     * 得到文件地址
     *
     * @param descDir      文件
     * @return {@link List}<{@link String}>
     */
    private static List<Path> getFilePaths(String descDir) {
        try (Stream<Path> paths = Files.walk(Paths.get(descDir))){
            return paths.filter(Files::isRegularFile).collect(Collectors.toList());
        } catch (IOException e) {
            System.out.println("获取文件地址失败！");
            return null;
        }
    }

    /**
     * 创建文件夹
     *
     * @param localPath 本地路径
     * @return boolean
     */
    public static boolean createFolder(String localPath) {
        //创建不同的文件夹目录  
        File folder = new File(localPath);
        //判断文件夹是否存在  
        if (!folder.exists())
        {
            //如果文件夹不存在，则创建新的的文件夹  
            folder.mkdirs();
        }
        return true;
    }

    /**
     * 下载文件
     *
     * @param httpUrl 远程地址
     * @param localPath 本地路径
     * @return file
     */
    public static String downloadNet(String httpUrl,String localPath) {        
        // 下载网络文件
        int bytesum = 0;
        int byteread = 0;

        URL url = null;
        URLConnection conn = null;
        InputStream inStream = null;
        FileOutputStream fs = null;
        try {
            url = new URL(httpUrl);
            conn = url.openConnection();
            inStream = conn.getInputStream();
            fs = new FileOutputStream(localPath);
            byte[] buffer = new byte[1204];
            while ((byteread = inStream.read(buffer)) != -1) {
                bytesum += byteread;
                fs.write(buffer, 0, byteread);
            }
        } catch (IOException e) {
            logger.error("文件下载流操作失败！",e);
        } catch (Exception e){
            logger.error("文件下载失败！",e);
        } finally {
            try{
                if(inStream != null){
                    inStream.close();
                }
                if(fs != null){
                    fs.close();
                }
            }catch (Exception ex){
                logger.error("文件下载流关闭失败！");
            }
        }
        return localPath;
    }

    /**
     * 获取文件后缀名
     * 效率慢、会出现不识别类型
     * @param httpUrl 远程地址
     * @return fileSuffix
     */
    public static String getFileSuffix(String httpUrl) {
        URL url = null;
        URLConnection conn = null;
        InputStream inStream = null;
        String suffix = "";
        try {
            url = new URL(httpUrl);
            conn = url.openConnection();
            inStream = conn.getInputStream();
            String suffixStr = conn.getContentType();
            String[] suffixs = suffixStr.split("/");
            //文件地址拼接后缀名
            suffix = suffixs[1];
            //小写
            suffix = suffix.toLowerCase();
            logger.info("获取文件链接后缀名成功[ZipUtil.getFileSuffix]：" + suffix);
        } catch (Exception e) {
            suffix = ERROR;
            logger.error("获取文件链接后缀名失败[ZipUtil.getFileSuffix]：",e);
        } finally {
            try {
                if(inStream != null){
                    inStream.close();
                }
            }catch (Exception ex){
                logger.error("获取文件链接后缀名,关闭流失败[ZipUtil.getFileSuffix]：",ex);
            }
        }
        return suffix;
    }
    /**
     * 获取文件后类型
     * 下载文件-判断类型-删除文件
     * @param httpUrl 远程地址
     * @return fileType（小写）
     */
    public static String getFileType(String httpUrl,String localFilePath) {
        try {
            //判断是否含本地文件夹，没有则创建
            createFolder(localFilePath);
            //拼接文件名称
            String filePath = localFilePath + "temporaryfile" + DateUtils.getNowDateLong();
            //将文件 temporaryfile 临时下载到本地
            downloadUsingStream(httpUrl,filePath);
            //判断本地文件类型
            File file = new File(filePath);
            Tika tika = new Tika();
            String mimeType = tika.detect(file);
            String[] mimeType2 = mimeType.split("/");
            //文件地址拼接后缀名
            String fileType = mimeType2[1].toLowerCase();
            logger.info("文件类型为：" + mimeType);
            //删除下载的本地文件
            deleteFile(localFilePath);
            return fileType;
        }catch (Exception e){
            logger.error("文件类型获取失败，原因：",e);
            return ERROR;
        }
    }

    /**
     * 获取文件后类型
     * 判断类型
     * @param localFilePath  下载本地址
     * @return fileType（小写）
     */
    public static String getFileType(String localFilePath) {
        try {
            //判断本地文件类型
            File file = new File(localFilePath);
            Tika tika = new Tika();
            String mimeType = tika.detect(file);
            String[] mimeType2 = mimeType.split("/");
            //文件地址拼接后缀名
            String fileType = mimeType2[1].toLowerCase();
            logger.info("文件类型为：" + mimeType);
            return fileType;
        }catch (Exception e){
            logger.error("文件类型获取失败，原因：",e);
            return ERROR;
        }
    }

    /**
     * 使用流下载文件
     *
     * @param httpUrl 远程地址
     * @return file
     */
    public static void downloadUsingStream(String httpUrl, String filePath){
        URL url;
        BufferedInputStream bis = null;
        FileOutputStream fis = null;
        try {
            url = new URL(httpUrl);
            bis = new BufferedInputStream(url.openStream());
            fis = new FileOutputStream(filePath);
            byte[] buffer = new byte[1024];
            int count = 0;
            while((count = bis.read(buffer,0,1024)) != -1)
            {
                fis.write(buffer, 0, count);
            }
            logger.info("使用流下载文件成功！");
        } catch(IOException e){
            logger.error("使用流下载文件失败,原因：",e);
        } finally {
            try {
                if(fis != null){
                    fis.close();
                }
                if(bis != null){
                    bis.close();
                }
            }catch (IOException e){
                logger.error("使用流下载文件关闭流时失败,原因：",e);
            }
        }
    }

    /**
     * 解压zip文件
     *
     * @param localPathNew 目标文件位置
     * @param descDir 解压后存放的位置
     * @return true/false
     */
    public static boolean unZip(String localPathNew, String descDir) {
        boolean flag = false;
        File zipFile = new File(localPathNew);
        File pathFile = new File(descDir);
        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }
        ZipFile zip = null;
        try {
            // 指定编码，否则压缩包里面不能有中文目录
            zip = new ZipFile(zipFile, Charset.forName("gbk"));
            for (Enumeration entries = zip.entries(); entries.hasMoreElements();) {
                ZipEntry entry = (ZipEntry) entries.nextElement();
                String zipEntryName = entry.getName();
                InputStream in = zip.getInputStream(entry);
                String outPath = (descDir + zipEntryName).replace("/",
                        File.separator);
                // 判断路径是否存在,不存在则创建文件路径
                File file = new File(outPath.substring(0,
                        outPath.lastIndexOf(File.separator)));
                if (!file.exists()) {
                    file.mkdirs();
                }
                // 判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
                if (new File(outPath).isDirectory()) {
                    continue;
                }

                OutputStream out = new FileOutputStream(outPath);
                byte[] buf1 = new byte[2048];
                int len;
                while ((len = in.read(buf1)) > 0) {
                    out.write(buf1, 0, len);
                }
                in.close();
                out.close();
            }
            flag = true;
            // 必须关闭，否则无法删除该zip文件
            zip.close();
        } catch (IOException e) {
            logger.error("解压zip文件流操作失败：",e);
        } catch (Exception e) {
            logger.error("解压zip文件失败：", e);
        }
        return flag;
    }

    // 删除文件 或者 清除文件夹下的所有文件
    public static void deleteFile(String filePath)
    {
        if(StrUtil.isEmpty(filePath)){
            return;
        }
        File file = new File(filePath);
        if (file.isFile())  //判断是否为文件，是，则删除
        {
            file.delete();
        }
        else //不为文件，则为文件夹
        {
            String[] childFilePath = file.list();//获取文件夹下所有文件相对路径
            for (String path:childFilePath)
            {
                deleteFile(file.getAbsoluteFile()+"/"+path);//递归，对每个都进行判断
            }
            file.delete(); // 如果不保留文件夹本身 则执行此行代码
        }
    }
    
    public static void main(String[] args) throws IOException {
        
        jaoyan();
        
//        deleteFile("D:/test/data");
    }
    
    public static void jaoyan() throws IOException {
        //删除上次的全部文件
        deleteFile("D:/NewsMergeFile/");
        
        //压缩包
        String urlStr = "https://ah-rrs.oss-cn-shanghai.aliyuncs.com/20221122040801653.zip?Expires=4822704483&OSSAccessKeyId=LTAI4FpavfCDPFoBXsW564ps&Signature=zO89zEk0VtZHN3LyxFyNz6VaFT0%3D";
        //图片
//        String urlStr = "https://ah-rrs.oss-cn-shanghai.aliyuncs.com/20200722064655732.jpg?Expires=4749014815&OSSAccessKeyId=LTAI4FpavfCDPFoBXsW564ps&Signature=Ucv14Ktix9vdim7SWe5w25ZhyGY%3D";
        
        String time = String.valueOf(System.currentTimeMillis());
        
        String localPathRoot = "D:/NewsMergeFile/";
        
        System.out.println("文件校验根目录：" + localPathRoot);
        
        // 文件存放位置
        String localPath = localPathRoot + System.currentTimeMillis();
        //创建不同的文件夹目录  
        createFolder(localPath);
        //下载文件
        /* File file = getNetUrlHttp(urlStr,localPath);*/
        
        String suffix = getFileSuffix(urlStr);
        
        if("zip".equals(suffix)){
            //下载压缩文件
            String localPathNew = downloadNet(urlStr,localPath+"/合并资料");
            
            String descDir = localPath+"/合并资料/";

            boolean flag = unZip(localPathNew, descDir);
            
            System.out.println("解压结果：" + flag);
            
            //如果解压成功
            if(flag){
                List<File> pathList = FileUtil.loopFiles(descDir);
                if(CollUtil.isNotEmpty(pathList)){
                    for (File file : pathList) {
                        String pathStr = file.getPath();
                        int startIndex = pathStr.lastIndexOf(".");
                        if(startIndex != -1){
                            //获取文件地址的后缀名称
                            String su = pathStr.substring(startIndex+1).toLowerCase();  
                            if("pdf".equals(su)){
                                //上传文件
                                /*String fileUrl = AliyunFileUtil.uploadFile(file,"finance");
                                if (StrUtil.isNotEmpty(fileUrl)){
                                    System.out.println("文件Url:" + fileUrl);
                                }*/
                                System.out.println("解压文件中包含PDF！");
                            }
                        }else {
                            System.out.println("获取文件地址失败！");
                        }
                    }
                }
            }
        }
    }
}
